/* Copyright 2009
 *
 * This program and the accompanying materials
 * are made available under the terms of the
 * Eclipse Public License v1.0 which accompanies
 * this distribution, and is available at
 *
 * 		http://www.eclipse.org/legal/epl-v10.html
 *
 * Unless required by applicable law or agreed to in
 * writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
 * OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * Contributors:
 * 	   IBM Corporation - initial API and implementation for JDT/DLTK
 *     Sean W. Quinn - initial adoption for use with PHP from various sources.
 */
package org.eclipse.php.internal.ui.text;

import java.util.Map;

import org.eclipse.core.runtime.Assert;
import org.eclipse.dltk.internal.ui.editor.EditorUtility;
import org.eclipse.dltk.internal.ui.editor.ScriptSourceViewer;
import org.eclipse.dltk.internal.ui.text.HTMLTextPresenter;
import org.eclipse.dltk.internal.ui.text.ScriptElementProvider;
import org.eclipse.dltk.ui.text.AbstractScriptScanner;
import org.eclipse.dltk.ui.text.IColorManager;
import org.eclipse.dltk.ui.text.ScriptPresentationReconciler;
import org.eclipse.dltk.ui.text.ScriptSourceViewerConfiguration;
import org.eclipse.dltk.ui.text.completion.ContentAssistPreference;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.AbstractInformationControlManager;
import org.eclipse.jface.text.DefaultInformationControl;
import org.eclipse.jface.text.IAutoEditStrategy;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.contentassist.ContentAssistant;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.information.IInformationPresenter;
import org.eclipse.jface.text.information.IInformationProvider;
import org.eclipse.jface.text.information.InformationPresenter;
import org.eclipse.jface.text.presentation.IPresentationReconciler;
import org.eclipse.jface.text.presentation.PresentationReconciler;
import org.eclipse.jface.text.rules.ITokenScanner;
import org.eclipse.jface.text.rules.RuleBasedScanner;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.php.internal.core.documentModel.partitioner.PHPPartitionTypesExt;
import org.eclipse.php.internal.ui.editor.contentassist.PHPCompletionProcessor;
import org.eclipse.php.internal.ui.text.completion.PHPContentAssistPreference;
import org.eclipse.php.internal.ui.typehierarchy.PHPHierarchyInformationControl;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.texteditor.ITextEditor;

/**
 * @author Sean W. Quinn (swquinn@gmail.com)
 */
@SuppressWarnings("restriction")
public class PHPSourceViewerConfiguration extends ScriptSourceViewerConfiguration {

  /** The PHP Text Tools. */
  private PHPTextTools phpTextTools;

  /** The document partitioning. */
  private String documentPartitioning;

  /** PHP source code scanner. */
  private PHPCodeScanner codeScanner;

  /** The PHP Single-Line Comment Scanner */
  private AbstractScriptScanner singlelineCommentScanner;

  /** The PHP String Scanner. */
  private AbstractScriptScanner quotedStringScanner;

  /** The PHPDoc Comment Scanner. */
  private AbstractScriptScanner phpDocScanner;

  /**
   * @return the PHP source code scanner for this configuration.
   */
  protected RuleBasedScanner getCodeScanner() {
    return codeScanner;
  }

  /**
   * @return the PHP singleline comment scanner for this configuration.
   */
  protected RuleBasedScanner getSinglelineCommentScanner() {
    return singlelineCommentScanner;
  }

  /**
   * @return the PHP quoted string scanner for this configuration.
   */
  protected ITokenScanner getQuotedStringScanner() {
    return quotedStringScanner;
  }

  /**
   * @return the PHPDoc scanner for this configuration.
   */
  protected ITokenScanner getPHPDocScanner() {
    return phpDocScanner;
  }

  /**
   *
   * @param colorManager the color manager
   * @param preferenceStore the preference store, can be read-only
   * @param editor the editor in which the configured viewer(s) will reside, or
   *          <code>null</code> if none
   * @param partitioning the document partitioning for this configuration, or
   *          <code>null</code> for the default partitioning
   */
  public PHPSourceViewerConfiguration(final IColorManager colorManager, final IPreferenceStore preferenceStore,
      final ITextEditor editor, final String partitioning) {
    super(colorManager, preferenceStore, editor, partitioning);
  }

  /**
   *
   */
  @Override
public String[] getConfiguredContentTypes(ISourceViewer sourceViewer) {
    return PHPPartitionTypesExt.PHP_PARTITION_TYPES;
  }

  /**
   * @return <code>true</code> iff the new setup without text tools is in use.
   */
  private boolean isNewSetup() {
    return phpTextTools == null;
  }

  @Override
protected void initializeScanners() {
    Assert.isTrue(isNewSetup());
    codeScanner = new PHPCodeScanner(getColorManager(), fPreferenceStore);
    quotedStringScanner = new PHPQuotedStringScanner(getColorManager(), fPreferenceStore);
    //singlelineCommentScanner = createCommentScanner(IStyleConstantsPHPExt.PHP_SINGLE_LINE_COMMENT, IStyleConstantsPHPExt.PHP_TODO_COMMENT);
    phpDocScanner = new PHPDocScanner(getColorManager(), fPreferenceStore);
  }

  /**
   *
   */
  @Override
public IPresentationReconciler getPresentationReconciler(final ISourceViewer sourceViewer) {
    PresentationReconciler reconciler = new ScriptPresentationReconciler();
    reconciler.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer));

    //    DefaultDamagerRepairer dr = new DefaultDamagerRepairer(codeScanner);
    //    reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE);
    //    reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE);
    //
    //    dr = new DefaultDamagerRepairer(getQuotedStringScanner());
    //    reconciler.setDamager(dr, PHPPartitionTypesExt.PHP_STRING);
    //    reconciler.setRepairer(dr, PHPPartitionTypesExt.PHP_STRING);
    //
    //    dr = new DefaultDamagerRepairer(getQuotedStringScanner());
    //    reconciler.setDamager(dr, PHPPartitionTypesExt.PHP_PERCENT_STRING);
    //    reconciler.setRepairer(dr, PHPPartitionTypesExt.PHP_PERCENT_STRING);
    //
    //    dr = new DefaultDamagerRepairer(getQuotedStringScanner());
    //    reconciler.setDamager(dr, PHPPartitionTypesExt.PHP_SINGLE_QUOTE_STRING);
    //    reconciler.setRepairer(dr, PHPPartitionTypesExt.PHP_SINGLE_QUOTE_STRING);
    //
    //    dr = new DefaultDamagerRepairer(getPHPDocScanner());
    //    reconciler.setDamager(dr, PHPPartitionTypesExt.PHP_DOC);
    //    reconciler.setRepairer(dr, PHPPartitionTypesExt.PHP_DOC);
    //
    //    dr = new DefaultDamagerRepairer(getSinglelineCommentScanner());
    //    reconciler.setDamager(dr, PHPPartitionTypesExt.PHP_COMMENT);
    //    reconciler.setRepairer(dr, PHPPartitionTypesExt.PHP_COMMENT);

    return reconciler;
  }

  /**
   * Adapts the behavior of the contained components to the change encoded in
   * the given event.
   * <p>
   * Clients are not allowed to call this method if the old setup with text
   * tools is in use.
   * </p>
   *
   * @param event the event to which to adapt
   * @see PHPSourceViewerConfiguration#PHPSourceViewerConfiguration(IColorManager,
   *      IPreferenceStore, ITextEditor, String)
   */
  @Override
public void handlePropertyChangeEvent(final PropertyChangeEvent event) {
    Assert.isTrue(isNewSetup());

    if (codeScanner.affectsBehavior(event)) {
      codeScanner.adaptToPreferenceChange(event);
    }

    if (quotedStringScanner.affectsBehavior(event)) {
      quotedStringScanner.adaptToPreferenceChange(event);
    }

    if (singlelineCommentScanner.affectsBehavior(event)) {
      singlelineCommentScanner.adaptToPreferenceChange(event);
    }

    if (phpDocScanner.affectsBehavior(event)) {
      phpDocScanner.adaptToPreferenceChange(event);
    }
  }

  /**
   * Determines whether the preference change encoded by the given event changes
   * the behavior of one of its contained components.
   *
   * @param event the event to be investigated
   * @return <code>true</code> if event causes a behavioral change
   */
  @Override
public boolean affectsTextPresentation(PropertyChangeEvent event) {
    return codeScanner.affectsBehavior(event) || quotedStringScanner.affectsBehavior(event)
        || singlelineCommentScanner.affectsBehavior(event) || phpDocScanner.affectsBehavior(event);
  }

  private IInformationControlCreator getHierarchyPresenterControlCreator(ISourceViewer sourceViewer) {
    return new IInformationControlCreator() {
      public IInformationControl createInformationControl(Shell parent) {
        int shellStyle = SWT.RESIZE;
        int treeStyle = SWT.V_SCROLL | SWT.H_SCROLL;
        return new PHPHierarchyInformationControl(parent, shellStyle, treeStyle);
      }
    };
  }

  /**
   * Returns the hierarchy presenter which will determine and shown type
   * hierarchy information requested for the current cursor position.
   *
   * @param sourceViewer the source viewer to be configured by this
   *          configuration
   * @param doCodeResolve a boolean which specifies whether code resolve should
   *          be used to compute the Java element
   * @return an information presenter
   */
  @Override
public IInformationPresenter getHierarchyPresenter(final ScriptSourceViewer sourceViewer, final boolean doCodeResolve) {
    // Do not create hierarchy presenter if there's no CU.
    if (getEditor() != null && getEditor().getEditorInput() != null
        && EditorUtility.getEditorInputModelElement(getEditor(), true) == null)
      return null;

    InformationPresenter presenter = new InformationPresenter(getHierarchyPresenterControlCreator(sourceViewer));
    presenter.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer));
    presenter.setAnchor(AbstractInformationControlManager.ANCHOR_GLOBAL);
    IInformationProvider provider = new ScriptElementProvider(getEditor(), doCodeResolve);
    presenter.setInformationProvider(provider, IDocument.DEFAULT_CONTENT_TYPE);

    presenter.setSizeConstraints(50, 20, true, false);
    return presenter;
  }

  @Override
public IAutoEditStrategy[] getAutoEditStrategies(ISourceViewer sourceViewer, String contentType) {
    // // TODO: check contentType. think, do we really need it? :)
    String partitioning = getConfiguredDocumentPartitioning(sourceViewer);
    return new IAutoEditStrategy[] { new PHPAutoEditStrategy(partitioning) };
  }

  /**
   * Returns the outline presenter control creator. The creator is a factory
   * creating outline presenter controls for the given source viewer. This
   * implementation always returns a creator for
   * <code>JavaOutlineInformationControl</code> instances.
   *
   * @param sourceViewer the source viewer to be configured by this
   *          configuration
   * @param commandId the ID of the command that opens this control
   * @return an information control creator
   */
  @Override
protected IInformationControlCreator getOutlinePresenterControlCreator(ISourceViewer sourceViewer,
      final String commandId) {
    return new IInformationControlCreator() {
      public IInformationControl createInformationControl(Shell parent) {
        int shellStyle = SWT.RESIZE;
        int treeStyle = SWT.V_SCROLL | SWT.H_SCROLL;
        return new PHPOutlineInformationControl(parent, shellStyle, treeStyle, commandId);
      }
    };
  }

  @Override
protected void alterContentAssistant(ContentAssistant assistant) {
    IContentAssistProcessor scriptProcessor = new PHPCompletionProcessor(getEditor(), assistant,
        IDocument.DEFAULT_CONTENT_TYPE);
    assistant.setContentAssistProcessor(scriptProcessor, IDocument.DEFAULT_CONTENT_TYPE);
    assistant.setContentAssistProcessor(scriptProcessor, PHPPartitionTypesExt.PHP_QUOTED_STRING);
  }

  @Override
protected ContentAssistPreference getContentAssistPreference() {
    return PHPContentAssistPreference.getDefault();
  }

  @Override
public IInformationControlCreator getInformationControlCreator(ISourceViewer sourceViewer) {
    return new IInformationControlCreator() {
      public IInformationControl createInformationControl(Shell parent) {
        return new DefaultInformationControl(parent, SWT.NONE, new HTMLTextPresenter(true), ""); //$NON-NLS-1$
      }
    };
  }

  @Override
protected void initializeQuickOutlineContexts(InformationPresenter presenter, IInformationProvider provider) {
    presenter.setInformationProvider(provider, PHPPartitionTypesExt.PHP_COMMENT);
    presenter.setInformationProvider(provider, PHPPartitionTypesExt.PHP_DOC);
    presenter.setInformationProvider(provider, PHPPartitionTypesExt.PHP_QUOTED_STRING);
  }

  @Override
protected Map getHyperlinkDetectorTargets(final ISourceViewer sourceViewer) {
    final Map targets = super.getHyperlinkDetectorTargets(sourceViewer);
    targets.put("org.eclipse.dltk.ruby.code", getEditor()); //$NON-NLS-1$
    return targets;
  }
}
